#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFontDatabase>
#include <QFileInfo>
#include <QFileDialog>
#include <QPrintDialog>
#include <QPrinter>
#include <QPrintPreviewDialog>
#include <QCloseEvent>
#include <QColorDialog>
#include <QTextCharFormat>
#include <QTextList>
#include <QTextEdit>

/* 主窗口构造函数
 * [功能] 初始化UI组件和核心功能 */
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)     // 继承主窗口特性
    , ui(new Ui::MainWindow)  // 创建UI实例
{
    ui->setupUi(this);        // 初始化UI组件
    init();                    // 执行自定义初始化
}

// 主窗口析构函数（释放UI资源）
MainWindow::~MainWindow() {
    delete ui;
}

/**
 * @brief 初始化主窗口组件和信号连接
 *
 * @details 完成以下初始化工作：
 * 1. 窗口基本属性设置
 * 2. 字体和滚动条配置
 * 3. 创建排他性动作组
 * 4. 建立动态菜单更新机制
 * 5. 连接子窗口激活信号
 */
void MainWindow::init()
{
    // 基础界面设置
    setWindowTitle("文档编辑器");
    ui->mdiArea->setBackground(QBrush(Qt::white)); // 设置MDI区域背景

    // 组件初始化
    initFontSize();      // 初始化字号选择框
    initScrollBar();     // 配置滚动条行为
    initWindowsAction(); // 设置窗口菜单初始状态

    // 信号连接：子窗口激活时更新界面状态
    connect(ui->mdiArea, &QMdiArea::subWindowActivated,
            this, &MainWindow::initWindowsAction);

    initDocAction();     // 初始化文本操作相关菜单

    // 创建排他性动作组（单选模式）
    group = new QActionGroup(this);
    group->setExclusive(true); // 确保同时只有一个动作被选中

    // 初始化信号映射器
    mapper = new QSignalMapper(this);

    // 建立动态菜单更新机制
    connect(ui->menu_W, &QMenu::aboutToShow,    // 菜单显示前触发
            this, &MainWindow::addSubWindowAction);
    connect(mapper, SIGNAL(mapped(QWidget *)),  // 映射动作到窗口激活
            this, SLOT(setActiveSubWindow(QWidget*)));
}

/* 初始化字号选择框
 * [功能] 填充系统标准字号
 * [注意] 使用QFontDatabase获取系统字体信息 */
void MainWindow::initFontSize()
{
    ui->comboBoxSize->clear();
    for(int size : QFontDatabase::standardSizes()) {
        ui->comboBoxSize->addItem(QString::number(size));
    }

    // 设置当前系统字号为默认选项
    QFont appFont = QApplication::font();
    ui->comboBoxSize->setCurrentText(QString::number(appFont.pointSize()));
}

/* 设置MDI区域滚动策略
 * [策略] 滚动条按需显示（节省空间） */
void MainWindow::initScrollBar()
{
    ui->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
    ui->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
}

/* 更新窗口相关菜单状态
 * [机制] 根据是否存在活动窗口控制功能可用性 */
void MainWindow::initWindowsAction()
{
    bool hasActive = activateSubWindow() != nullptr;
    ui->actionSave->setEnabled(hasActive);
    ui->actionSaveAs->setEnabled(hasActive);
    ui->actionPrint->setEnabled(hasActive);
    ui->actionPrintSee->setEnabled(hasActive);
    ui->actionUndo->setEnabled(hasActive);
    ui->actionRedo->setEnabled(hasActive);
    ui->actionClose->setEnabled(hasActive);
    ui->actionCloseAll->setEnabled(hasActive);
    ui->actionCascade->setEnabled(hasActive);
    ui->actionTile->setEnabled(hasActive);
    ui->actionNext->setEnabled(hasActive);
    ui->actionPrevious->setEnabled(hasActive);

}

/* 获取当前活动编辑器
 * [返回] TextEdit指针（无活动窗口时返回nullptr） */
TextEdit *MainWindow::activateSubWindow()
{
    if(QMdiSubWindow* win = ui->mdiArea->activeSubWindow()) {
        return qobject_cast<TextEdit*>(win->widget());
    }
    return nullptr;
}

/* 初始化文本操作菜单状态
 * [初始] 禁用所有格式操作（等待文本选中） */
void MainWindow::initDocAction()
{
    setDocActionStatus(false);
}

/* 新建文档响应槽
 * [流程]
 * 1. 创建编辑器实例
 * 2. 添加至MDI区域
 * 3. 连接选择文本信号
 * 4. 初始化新文档属性 */
void MainWindow::on_actionNew_triggered()
{
    TextEdit* editor = new TextEdit;
    ui->mdiArea->addSubWindow(editor);

    connect(editor, &QTextEdit::copyAvailable,
           this, &MainWindow::setDocActionStatus);

    editor->initNewDoc();
    editor->show();
}

/* 设置格式操作按钮状态
 * [参数] b - 是否启用操作
 * [关联] 文本选中状态变化时触发 */
void MainWindow::setDocActionStatus(bool b)
{
    ui->actionCut->setEnabled(b);
    ui->actionCopy->setEnabled(b);
    ui->actionColors->setEnabled(b);
    ui->actionBold->setEnabled(b);
    ui->actionItalic->setEnabled(b);
    ui->actionUnderLine->setEnabled(b);
    ui->actionLeft->setEnabled(b);
    ui->actionRight->setEnabled(b);
    ui->actionCenter->setEnabled(b);
    ui->actionJustify->setEnabled(b);
}

/* 打开文件响应槽
 * [流程]
 * 1. 获取文件路径
 * 2. 检查是否已打开
 * 3. 创建新编辑器
 * 4. 尝试加载文件
 * [注意] 错误处理需优化（当前直接删除可能异常） */
void MainWindow::on_actionOpen_triggered()
{
    QString path = QFileDialog::getOpenFileName(this, "打开文件",
        QDir::homePath(), "All Files (*.*)");

    if(path.isEmpty()) return;

    // 检查重复打开
    if(QMdiSubWindow* exist = findSubWindow(path)) {
        ui->mdiArea->setActiveSubWindow(exist);
        return;
    }

    TextEdit* editor = new TextEdit;
    QMdiSubWindow* sub = ui->mdiArea->addSubWindow(editor);

    if(editor->loadDocument(path)) {
        connect(editor, &QTextEdit::copyAvailable,
               this, &MainWindow::setDocActionStatus);
        sub->show();
        ui->statusbar->showMessage("已打开: " + path, 3000);
    } else {
        // 危险操作！可能造成内存访问异常
        ui->mdiArea->removeSubWindow(sub);
        delete sub;
    }
}

/* 查找已存在的文档窗口
 * [参数] fileName - 文件路径
 * [机制] 通过规范路径比对避免重复打开 */
QMdiSubWindow *MainWindow::findSubWindow(const QString &fileName)
{
    QString canonical = QFileInfo(fileName).canonicalFilePath();
    for(QMdiSubWindow* win : ui->mdiArea->subWindowList()) {
        if(TextEdit* edit = qobject_cast<TextEdit*>(win->widget())) {
            if(edit->getFilePath() == canonical) {
                return win;
            }
        }
    }
    return nullptr;
}

/*
 * [槽函数] 响应保存动作
 * [功能] 保存当前活动文档
 * [流程]
 * 1. 获取当前活动文本编辑器实例
 * 2. 调用保存核心功能
 * 3. 成功保存后更新状态栏提示
 * [注意] 未处理保存失败情况（对话框取消/写入错误）
 */
void MainWindow::on_actionSave_triggered()
{
    if(TextEdit* edit = activateSubWindow()){  // 获取当前活动窗口
        if(edit->saveDoc())                    // 执行保存操作
        {
            ui->statusbar->showMessage("保存成功", 3000); // 显示临时状态
        }
        // 缺少else处理保存失败情况
    }
}

/*
 * [槽函数] 响应另存为动作
 * [功能] 将文档保存为新文件
 * [流程]
 * 1. 获取当前活动编辑器
 * 2. 通过文件对话框获取新路径
 * 3. 执行写入操作
 * 4. 更新状态栏提示
 * [注意] 未处理文件对话框取消操作
 */
void MainWindow::on_actionSaveAs_triggered()
{
    if(TextEdit* edit = activateSubWindow()){
        if(edit->saveAsDoc())  // 执行另存为操作
        {
            ui->statusbar->showMessage("保存成功", 3000);
        }
    }
}

void MainWindow::on_actionPrint_triggered()
{
    TextEdit* edit = activateSubWindow();
    if(!edit) return;

    //QPrinter::HighResolution: 将printer的分辨率设置为当前打印机的分辨率
    QPrinter printer(QPrinter::HighResolution);

    QPrintDialog printDialog(&printer, this);
    printDialog.setOption(QPrintDialog::PrintPageRange);
    printDialog.setPrintRange(QPrintDialog::CurrentPage);

    if(printDialog.exec() == QPrintDialog::Accepted) {
        edit->print(&printer);
    }
}

void MainWindow::on_actionPrintSee_triggered()
{
    TextEdit* edit = activateSubWindow();
    if(!edit) return;

    QPrinter printer(QPrinter::HighResolution);
    QPrintPreviewDialog dialog(&printer, this);

    //paintRequested: 需要生成一组预览界面时，就会发出这种信号
    connect(&dialog, &QPrintPreviewDialog::paintRequested, this, &MainWindow::paintPreview);

    if(dialog.exec() == QPrintPreviewDialog::Accepted) {
        edit->print(&printer);
    }
}

void MainWindow::paintPreview(QPrinter *printer)
{
    TextEdit* edit = activateSubWindow();
    if(edit)
    {
        edit->print(printer);
    }
}

void MainWindow::on_actionUndo_triggered()
{
    TextEdit* edit = activateSubWindow();
    if(edit)
    {
        edit->undo();
    }
}

void MainWindow::on_actionRedo_triggered()
{

    TextEdit* edit = activateSubWindow();
    if(edit)
    {
        edit->redo();
    }
}

void MainWindow::on_actionCut_triggered()
{

    TextEdit* edit = activateSubWindow();
    if(edit)
    {
        edit->cut();
    }
}

void MainWindow::on_actionCopy_triggered()
{
    TextEdit* edit = activateSubWindow();
    if(edit)
    {
        edit->copy();
    }
}

void MainWindow::on_actionPaste_triggered()
{
    TextEdit* edit = activateSubWindow();
    if(edit)
    {
        edit->paste();
    }
}

/**
 * @brief 主窗口关闭事件处理
 * @param event 关闭事件对象
 *
 * @details 关闭流程：
 * 1. 先关闭所有MDI子窗口
 * 2. 如果仍有未关闭的子窗口，取消关闭操作
 * 3. 否则允许关闭主窗口
 *
 * @note 通过 event->ignore() 可以阻止窗口关闭
 */
void MainWindow::closeEvent(QCloseEvent* event)
{
    ui->mdiArea->closeAllSubWindows();  // 关闭所有子窗口
    if (ui->mdiArea->currentSubWindow()) {  // 检查是否存在未关闭的子窗口
        event->ignore();  // 取消关闭操作
    } else {
        event->accept();  // 允许关闭主窗口
    }
}

/**
 * @brief 退出程序菜单项槽函数
 * @note 通过调用 close() 触发 closeEvent 事件
 */
void MainWindow::on_actionExit_triggered()
{
    close();  // 触发主窗口关闭流程
}


void MainWindow::on_actionClose_triggered()
{
    ui->mdiArea->closeActiveSubWindow();
}

void MainWindow::on_actionCloseAll_triggered()
{
    ui->mdiArea->closeActiveSubWindow();
}

void MainWindow::on_actionTile_triggered()              //平铺
{
    ui->mdiArea->tileSubWindows();
}

void MainWindow::on_actionCascade_triggered()           //层叠
{
    ui->mdiArea->cascadeSubWindows();
}

void MainWindow::on_actionNext_triggered()               //下一个子窗口
{
    ui->mdiArea->activateNextSubWindow();
}

void MainWindow::on_actionPrevious_triggered()           //上一个子窗口
{
    ui->mdiArea->activatePreviousSubWindow();
}


/**
 * @brief 动态更新窗口菜单中的子窗口列表
 *
 * @details 在菜单显示前自动调用，完成以下操作：
 * 1. 清空现有窗口列表动作
 * 2. 为每个子窗口创建可勾选菜单项
 * 3. 设置当前激活窗口的勾选状态
 * 4. 绑定菜单项到窗口激活功能
 *
 * @note 通过QSignalMapper实现多个动作映射到同一槽函数
 */
void MainWindow::addSubWindowAction()
{
    // 清理现有动作组
    auto actionList = group->actions();
    if(!actionList.isEmpty()) {
        for(auto action : actionList) {
            delete action; // 释放内存防止泄漏
        }
    }

    // 获取当前所有子窗口
    auto subWindowList = ui->mdiArea->subWindowList();
    if(!subWindowList.isEmpty()) ui->menu_W->addSeparator(); // 添加分割线

    // 为每个子窗口创建菜单动作
    int i = 0;
    for(auto& subWindow : subWindowList)
    {
        TextEdit* edit = qobject_cast<TextEdit*>(subWindow->widget()); // 安全类型转换
        //QString action_text = QString("%1 %2").arg(++i).arg(edit->getDocName());
        QString action_text = edit->getDocName();

        // 创建可勾选动作
        QAction* action = ui->menu_W->addAction(action_text);
        group->addAction(action);
        action->setCheckable(true);
        if(edit == activateSubWindow()) action->setChecked(true); // 标记当前激活窗口

        // 信号映射配置：将动作与对应子窗口关联
        connect(action, SIGNAL(triggered(bool)), mapper, SLOT(map()));
        mapper->setMapping(action, subWindow); // 建立动作到窗口的映射关系
    }
}

/**
 * @brief 设置活动子窗口
 * @param widget 包含子窗口的QWidget对象
 */
void MainWindow::setActiveSubWindow(QWidget* widget)
{
    auto* subWindow = qobject_cast<QMdiSubWindow*>(widget); // 转换窗口类型
    ui->mdiArea->setActiveSubWindow(subWindow); // 激活指定子窗口
}


// ==================== 字体样式设置槽函数 ====================

/**
 * @brief 颜色设置菜单项触发槽函数
 *
 * 功能流程：
 * 1. 获取当前激活的文本编辑子窗口
 * 2. 弹出颜色选择对话框获取用户选择的颜色
 * 3. 设置文本前景色并更新工具栏图标
 */
void MainWindow::on_actionColors_triggered()
{
    // 获取当前激活的文本编辑组件
    auto edit = activateSubWindow();
    if(edit)
    {
        // 获取当前字符格式
        QTextCharFormat format = edit->currentCharFormat();

        // 获取当前颜色（默认使用现有颜色）
        QColor color = format.foreground().color();

        // 弹出颜色选择对话框（模态）
        color = QColorDialog::getColor(color, nullptr, "选择颜色");

        if(color.isValid()) {  // 验证颜色有效性（用户可能取消选择）
            // 设置新的前景色画刷
            format.setForeground(QBrush(color));

            // 合并格式到当前选区或光标位置
            edit->mergeCurrentCharFormat(format);

            // 更新工具栏颜色按钮图标（显示当前颜色）
            QPixmap pixmap(24, 24);
            pixmap.fill(color);  // 创建纯色图标
            ui->actionColors->setIcon(pixmap);
        }
    }
}

/**
 * @brief 加粗样式切换槽函数
 * @param checked 是否启用加粗（来自QAction的触发状态）
 *
 * 操作流程：
 * 1. 获取当前文本编辑组件
 * 2. 修改字体加粗属性
 * 3. 应用新格式到当前选区
 */
void MainWindow::on_actionBold_triggered(bool checked)
{
    if(auto edit = activateSubWindow()) {
        QTextCharFormat format = edit->currentCharFormat();
        QFont font = format.font();

        // 设置/取消加粗样式
        font.setBold(checked);

        // 更新格式并合并到文档
        format.setFont(font);
        edit->mergeCurrentCharFormat(format);
    }
}

/**
 * @brief 斜体样式切换槽函数
 * @param checked 是否启用斜体
 */
void MainWindow::on_actionItalic_triggered(bool checked)
{
    if(auto edit = activateSubWindow()) {
        QTextCharFormat format = edit->currentCharFormat();
        QFont font = format.font();

        // 设置/取消斜体样式
        font.setItalic(checked);

        format.setFont(font);
        edit->mergeCurrentCharFormat(format);
    }
}

/**
 * @brief 下划线样式切换槽函数
 * @param checked 是否启用下划线
 */
void MainWindow::on_actionUnderLine_triggered(bool checked)
{
    if(auto edit = activateSubWindow()) {
        QTextCharFormat format = edit->currentCharFormat();
        QFont font = format.font();

        // 设置/取消下划线
        font.setUnderline(checked);

        format.setFont(font);
        edit->mergeCurrentCharFormat(format);
    }
}

// ==================== 字体格式设置槽函数 ====================

/**
 * @brief 左对齐菜单项触发槽函数
 *
 * 功能流程：
 * 1. 获取当前激活的文本编辑子窗口
 * 2. 设置当前段落或选中段落的对齐方式为左对齐
 */
void MainWindow::on_actionLeft_triggered()
{
    if(auto edit = activateSubWindow()) {
        // 使用Qt::AlignLeft枚举值设置对齐方式
        edit->setAlignment(Qt::AlignLeft);
    }
}

/**
 * @brief 居中对齐菜单项触发槽函数
 *
 * @note 对齐操作作用于：
 * - 当前光标所在段落
 * - 或选中的多个段落
 */
void MainWindow::on_actionCenter_triggered()
{
    if(auto edit = activateSubWindow()) {
        // Qt::AlignCenter 表示居中对齐模式
        edit->setAlignment(Qt::AlignCenter);
    }
}

/**
 * @brief 右对齐菜单项触发槽函数
 *
 * @details 对齐效果遵循以下规则：
 * 1. 当光标在段落中但未选择文本时，影响整个段落
 * 2. 当存在文本选择时，影响所有被选中的段落
 */
void MainWindow::on_actionRight_triggered()
{
    if(auto edit = activateSubWindow()) {
        edit->setAlignment(Qt::AlignRight);
    }
}

/**
 * @brief 两端对齐菜单项触发槽函数
 *
 * @note 特别注意：
 * - 在Qt中，Qt::AlignJustify 对齐方式会在单词间添加适当空格
 * - 对最后一行文本的处理方式与常规文字处理软件一致（左对齐）
 */
void MainWindow::on_actionJustify_triggered()
{
    if(auto edit = activateSubWindow()) {
        edit->setAlignment(Qt::AlignJustify);
    }
}

/**
 * @brief 处理组合框选择列表样式的事件
 * @param index 用户选择的组合框索引（0-8，0为清除列表，1-8对应不同列表格式）
 *
 * 功能说明：
 * 1. 将组合框索引映射为Qt的列表样式枚举
 * 2. 处理当前文本块列表格式的切换：
 *    - 如果当前已是列表项，先清除原有列表关联
 *    - 创建新的列表格式
 *    - 支持8种列表样式切换（包括取消列表格式）
 */
void MainWindow::on_comboBox_activated(int index)
{
    /* 列表样式枚举映射关系：
     * 索引 | Qt列表样式              | 显示效果
     * ----|------------------------|-----------
     * 0   | ListStyleUndefined     | 清除列表格式
     * 1   | ListDisc               | • 项目符号
     * 2   | ListCircle             | ◦ 空心圆
     * 3   | ListSquare             | ▪ 方块
     * 4   | ListDecimal            | 1. 数字编号
     * 5   | ListLowerAlpha         | a. 小写字母
     * 6   | ListUpperAlpha         | A. 大写字母
     * 7   | ListLowerRoman         | i. 小写罗马
     * 8   | ListUpperRoman         | I. 大写罗马
     */

    // 获取当前活动的文本编辑器窗口
    TextEdit* edit = activateSubWindow();
    if(edit)
    {
        // 处理清除列表格式的情况（索引0）
        if(index == 0)
        {
           QTextCursor cursor = edit->textCursor();
           cursor.beginEditBlock(); // 开始组合操作（支持单次撤销）

           // 获取当前块格式和列表对象
           QTextBlockFormat blockFormat = cursor.blockFormat();
           QTextList* list = cursor.currentList();

           if(list)
           {
               // 从列表中移除当前块，解除列表关联
               list->remove(cursor.block());
               // 重置缩进避免格式残留
               blockFormat.setIndent(0);
               cursor.setBlockFormat(blockFormat);
           }

           cursor.endEditBlock(); // 结束组合操作
        }

        // 将组合框索引转换为Qt列表样式
        QTextListFormat::Style style = QTextListFormat::ListStyleUndefined;
        switch (index) {
        case 1: style = QTextListFormat::ListDisc; break;      // • 实心圆点
        case 2: style = QTextListFormat::ListCircle; break;    // ◦ 空心圆圈
        case 3: style = QTextListFormat::ListSquare; break;    // ▪ 实心方块
        case 4: style = QTextListFormat::ListDecimal; break;  // 1. 十进制数字
        case 5: style = QTextListFormat::ListLowerAlpha; break;// a. 小写字母
        case 6: style = QTextListFormat::ListUpperAlpha; break;// A. 大写字母
        case 7: style = QTextListFormat::ListLowerRoman; break;// i. 小写罗马数字
        case 8: style = QTextListFormat::ListUpperRoman; break;// I. 大写罗马数字
        }

        // 仅在选择有效列表样式时执行格式设置
        if (style != QTextListFormat::ListStyleUndefined)
        {
            QTextCursor cursor = edit->textCursor();
            cursor.beginEditBlock(); // 开始组合操作（支持单次撤销）

            // 获取当前块格式并初始化列表格式
            QTextBlockFormat blockFormat = cursor.blockFormat();
            QTextListFormat listFormat;

            // 检查当前是否处于列表项中
            if (auto list = cursor.currentList())
            {
                // 获取现有列表格式
                listFormat = list->format();
                // 从旧列表中移除当前块，解除关联
                list->remove(cursor.block());

                // 重置缩进为0，防止新旧格式叠加
                blockFormat.setIndent(0);
                cursor.setBlockFormat(blockFormat);
            }

            // 配置新列表格式并创建列表
            listFormat.setStyle(style);
            cursor.createList(listFormat); // 创建新列表并关联当前块

            cursor.endEditBlock(); // 结束组合操作
        }
    }
}

/* 代码逻辑详解：
1. 原子操作管理：
   - 使用beginEditBlock()和endEditBlock()将多个格式修改操作包装为原子操作
   - 确保用户可以通过一次撤销操作回退所有格式变更

2. 列表关联处理：
   - 在创建新列表前，必须通过remove()解除当前块与旧列表的关联
   - 防止出现多个列表同时作用于同一文本块的情况

3. 缩进重置机制：
   - 修改列表样式时主动重置缩进为0，避免因继承旧列表缩进导致的格式错位
   - Qt的列表缩进是自动管理的，手动重置确保格式一致性

4. 样式切换策略：
   - 即使选择相同列表样式也会重新创建列表，确保格式刷新
   - ListStyleUndefined作为清除列表的特殊标识

5. 光标状态检测：
   - cursor.currentList()动态检测光标所在块的列表状态
   - 支持在列表和非列表状态之间自由切换
*/
void MainWindow::on_fontComboBox_activated(const QString &arg1)
{
    TextEdit* edit = activateSubWindow();
    if(edit)
    {
        QTextCharFormat format;
        format.setFontFamily(arg1);
        edit->mergeCurrentCharFormat(format);
    }
}

void MainWindow::on_comboBoxSize_activated(const QString &arg1)
{
    TextEdit* edit = activateSubWindow();
    if(edit)
    {
        QTextCharFormat format;
        format.setFontPointSize(arg1.toInt());
        edit->mergeCurrentCharFormat(format);
    }
}
